﻿// Inner Fire 游戏引擎库
// Avatar - 通用骨架。通用骨骼（Genetic Skeleton）的躯体(Avatar)
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于万用（通用）骨骼的印射。将不同种类的动画中的命名统一成一个通用骨骼（Genetic
// Skeleton）。为了统一，命名和Unity或是UE中的命名一致。
// 这样我就能够从Unity或者UE里面编写一个脚本，用来打印我的XML印射，能够省下不少事情。
// 毕竟我现在没有自已的编辑器，等以后用three.js开发编辑器之后，也不用现在手写这么麻烦了。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-29

#pragma once

#include "armature_node.h"
#include "avatar_id.h"
#include <EASTL/unordered_map.h>
#include <EASTL/vector.h>
#include <game/animation/avatar_attach.h>
#include <game/config/config_info.h>
#include <graphics/transform_def.h>
#include <xml_reader.h>

struct aiNode;

namespace ifire::game {
class ConfigManager;
class PhysicsBoneSetting;

struct AvatarMapping {
  AvatarMapping(ikit::sstr _bone, int _id);
  AvatarMapping(const AvatarGroup* group, const ikit::XmlNode& node);

  ikit::sstr bone;
  int id;
};

struct AvatarPoseBone {
  ikit::sstr bone;
  Trans2 transform;
  DirectX::XMMATRIX final_mat;

  AvatarPoseBone();
  AvatarPoseBone(const ikit::XmlNode& node);
};

struct AvatarPhysicsBone {
  // 此项目的根骨骼
  ikit::sstr bone;
  // 此项目对齐的设置
  ikit::sstr setting_name;
  // 此项目的设置
  PhysicsBoneSetting* setting;

  AvatarPhysicsBone();
  AvatarPhysicsBone(const ikit::XmlNode& node);
};

class Avatar : public ConfigInfo {
public:
  Avatar(ConfigManager* manager, const ikit::XmlNode& node);
  Avatar(const Avatar& lhs) = delete;
  Avatar& operator=(const Avatar& lhs) = delete;
  ~Avatar() = default;

  // 通过名称查找一个Map
  int FindId(ikit::sstr name) const;

  // 通过Map查找一个名称
  ikit::sstr FindBone(int id) const;

  // 通过Slot，查找一个应该绑定的项目。
  const AvatarAttach* GetAttachSlot(ikit::sstr slot_name) const;

  // 通过PoseBone的名称获得项目（可以做修改）
  AvatarPoseBone& GetPoseBone(ikit::sstr name);
  // 通过PoseBone的名称获得项目
  const AvatarPoseBone& FindPoseBone(ikit::sstr name) const;
  // 通过Avatar获得一个PoseBone，如果不存在，返回一个空的PoseBone
  const AvatarPoseBone& FindPoseBone(int id) const;

  // 计算与另外一个矩阵的DIFF
  void CalcDiff(Avatar* b, MatrixList& list);

  // 打印这个躯体的信息
  void Print() const;
  // 与一个骨架对比，打印为XML格式
  void PrintXml(const ArmatureTree* target, const char* file_name) const;

  const ArmatureTree* GetNodeTree() const { return tree_.get(); }
  eastl::vector<AvatarPoseBone>& PoseBones() { return pose_bones_; }
  const eastl::vector<AvatarPhysicsBone>& PhysicsBones() const {
    return physics_bones_;
  }
  const AvatarGroup* GetGroup() const { return group_; }

  // 进行装配
  void Setup() override;

private:
  // 从文件中载入信息
  void Load();
  // 计算与另外一个矩阵的DIFF（层级）
  void CalcDiffHierarchy(DirectX::FXMMATRIX parent_mat, ArmatureNode* node,
      Avatar* b, MatrixList& list);

  // 此Avatar所使用的Group
  const AvatarGroup* group_;

  eastl::vector<AvatarMapping> mapping_;
  std::string pose_file_;
  std::unique_ptr<ArmatureTree> tree_;
  // 定位到Pose的骨骼数据
  eastl::vector<AvatarPoseBone> pose_bones_;
  // 用于快速访问的骨骼数据
  eastl::unordered_map<size_t, size_t> pose_bones_mapping_;
  // 用于武器的配置
  eastl::vector<std::unique_ptr<AvatarAttach>> attachs_;
  // 用于动态骨骼
  eastl::vector<AvatarPhysicsBone> physics_bones_;

  // 此Avatar是否需要变换单位
  bool convert_units_;
};

using AvatarPtr = std::unique_ptr<Avatar>;

} // namespace ifire::game